#!/bin/bash

cd "$(dirname "$0")"

usage() {
    echo "Usage: $0 [ /path/to/dist ] # default location is dist"
    echo "    [ --s3 | --lmdb | --level | --sftp | --sqlite | --mysql | --oracle | --mysql | --pgsql ] # bundle 1 or more storage engine"
    echo "    [ --dev ]  # avoid minification, add verbosity to esbuild/npm"
    echo "    [ --versbose ]  # add verbosity"
    echo "    [ --tool ]  # storage migration and repair tools"
    echo "    [ --restart ]  # restart cronicle upon bundle completion (in dev mode)"
    echo "    [ --engine engine ] # copy sample_conf/example/storage.engine.json to dist/conf/storage.json (engine: fs/sqlite/s3/...)"
    echo "    [ --test ]  # run unit test upon bundle completion"
    exit 1
}

writehead() {
    printf "\n ---- $1 [$(date +"%T")]\n"    
}


# check for custom nodejs binary
if [ -f $PWD/nodejs/bin/node ]; then
      export PATH="$PWD/nodejs/bin:$PATH"
      echo "using custom node version: $(node -v)"
fi

# -------------------- ARG CHECK 
x=0 
dist="dist"  # default

while (( "$#" )); do
    case $1 in
        --s3 | --S3 ) s3=1 ;;
        --sftp )  sftp=1 ;;
        --lmdb )  lmdb=1 ;;
        --level ) level=1 ;;
        --sql )   sql=1 ;;
        --oracle ) oracle=1 ;;
        --mssql ) mssql=1 ;;
        --mysql ) mysql=1 ;;
        --pgsql ) pgsql=1 ;;
        --sqlite ) sqlite=1 ;;
        --test ) test=1 ;; # run unit test at the end
        --dev | -d )  dev=1 ;; # avoid minification, add verbosity
        --verbose | -v )  verbose=1 ;;
        --force | -f )  force=1 ;; # reinstall npm packages
        --tools | -t ) tools=1 ;; # bundle up repair and migration tools
        --all | -a ) all=1 ;; # install all engines
        --restart | -r ) restart=1 ;; # start cronicle upon completion
        --engine ) shift; engine=$1 ;;
        --help ) usage ;;
        -*) echo "invalid parameter: $1"; usage ;;
        * ) dist=$1; x=$(($x + 1))
    esac
shift
done


if [ "$x" -gt 1 ]; then
  echo "Too many arguments"; usage;
fi


if ! command -v npm &>/dev/null ; then
    echo "npm is not installed"
    exit 1
fi

if ! command -v node &>/dev/null ; then
    echo "node is not installed"
    exit 1
fi

# ---------------------------------------------------------------------

echo ""
echo -e "\033[1;4mInstalling cronicle bundle into $(readlink -f $dist)\033[0m"
echo ""

# debug settings
minify="--minify=true"
ESBuildLogLevel="warning"
npmLogLevel="warn"

do_restart="false"
if [ "$restart" = 1 ]; then
 do_restart="true"
fi

# check if cronicle is running and stop
if [ -e $dist/logs/cronicled.pid ]; then 
  if ps -p $(cat $dist/logs/cronicled.pid) > /dev/null; then
    writehead "Stopping cronicle [auto restart: $do_restart]"
    kill -15 $(cat $dist/logs/cronicled.pid)  # sigterm
  fi
fi
  
if [ "$dev" = 1 ]; then
  minify="--minify=false"
  ESBuildLogLevel="info"
  npmLogLevel="info"
fi

if [ "$restart" = 1 ]; then
  minify="--minify=false"
fi

if [ "$verbose" = 1 ]; then
  ESBuildLogLevel="info"
  npmLogLevel="info"
fi

if [ "$all" = 1 ]; then
  s3=1
  level=1
  lmdb=1
  sql=1
  sftp=1
  tools=1
fi


# ---------------------------------------------------------

# install esbuild if needed
if ! command -v esbuild &>/dev/null; then
  # writehead "Installing esbuild"
  # npm i esbuild -g --loglevel $npmLogLevel
  export PATH="./node_modules/esbuild/bin:$PATH"
fi

# skip "npm install" if node_modules exist already
if [ ! -d "node_modules" ] || [ "$force" = 1 ]; then
  writehead "Installing npm packages"
  npm install --loglevel $npmLogLevel
fi

# ----------------

mkdir -p $dist/bin
cp -r htdocs $dist/
# cp -r bin  $dist/
cp package.json  $dist/bin/
cp bin/manager bin/worker bin/cronicled.init bin/control.sh bin/getnode.sh $dist/bin/
  
# ------------------------------- 

writehead "Building frontend"


mkdir -p $dist/htdocs/js/external && cp \
 node_modules/jquery/dist/jquery.min.js \
 node_modules/moment/min/moment.min.js \
 node_modules/moment-timezone/builds/moment-timezone-with-data.min.js \
 node_modules/chart.js/dist/Chart.min.js \
 node_modules/jstimezonedetect/dist/jstz.min.js \
 node_modules/socket.io/client-dist/socket.io.min.js \
 node_modules/ansi_up/ansi_up.js \
 node_modules/jquery-ui-dist/jquery-ui.min.js \
 node_modules/graphlib/dist/graphlib.min.js \
 node_modules/vis-network/dist/vis-network.min.js \
 node_modules/xss/dist/xss.min.js \
 node_modules/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js \
 node_modules/diff/dist/diff.min.js \
 $dist/htdocs/js/external/

mkdir -p $dist/htdocs/css && cp \
  node_modules/font-awesome/css/font-awesome.min.css \
  node_modules/@mdi/font/css/materialdesignicons.min.css \
  node_modules/jquery-ui-dist/jquery-ui.min.css \
  node_modules/jquery-datetimepicker/build/jquery.datetimepicker.min.css \
  node_modules/pixl-webapp/css/base.css \
 $dist/htdocs/css/

mkdir -p $dist/htdocs/fonts && cp \
 node_modules/font-awesome/fonts/*.woff2 \
 node_modules/@mdi/font/fonts/*.woff2 \
 node_modules/pixl-webapp/fonts/*.woff2 \
 $dist/htdocs/fonts/

# code mirror css

cat \
	node_modules/codemirror/lib/codemirror.css \
	node_modules/codemirror/theme/darcula.css \
	node_modules/codemirror/theme/solarized.css \
	node_modules/codemirror/theme/gruvbox-dark.css \
  node_modules/codemirror/theme/base16-dark.css \
  node_modules/codemirror/theme/ambiance.css \
  node_modules/codemirror/theme/nord.css \
	node_modules/codemirror/addon/scroll/simplescrollbars.css \
	node_modules/codemirror/addon/display/fullscreen.css \
	node_modules/codemirror/addon/lint/lint.css \
	node_modules/codemirror/addon/fold/foldgutter.css \
  >  $dist/htdocs/css/codemirror.css

# codemirror js
cat \
	 node_modules/codemirror/lib/codemirror.js \
	 node_modules/codemirror/addon/scroll/simplescrollbars.js \
	 node_modules/codemirror/addon/edit/matchbrackets.js \
	 node_modules/codemirror/addon/selection/active-line.js \
	 node_modules/codemirror/addon/fold/foldgutter.js \
	 node_modules/codemirror/addon/fold/foldcode.js \
	 node_modules/codemirror/addon/fold/brace-fold.js \
	 node_modules/codemirror/addon/fold/indent-fold.js \
	 node_modules/codemirror/mode/powershell/powershell.js \
	 node_modules/codemirror/mode/javascript/javascript.js \
	 node_modules/codemirror/mode/python/python.js \
	 node_modules/codemirror/mode/perl/perl.js \
	 node_modules/codemirror/mode/shell/shell.js \
	 node_modules/codemirror/mode/groovy/groovy.js \
	 node_modules/codemirror/mode/clike/clike.js \
	 node_modules/codemirror/mode/properties/properties.js \
	 node_modules/codemirror/addon/display/fullscreen.js \
   node_modules/codemirror/addon/display/placeholder.js \
	 node_modules/codemirror/mode/xml/xml.js \
	 node_modules/codemirror/mode/sql/sql.js \
   node_modules/js-yaml/dist/js-yaml.js \
	 node_modules/codemirror/addon/lint/lint.js \
	 node_modules/codemirror/addon/lint/json-lint.js \
	 node_modules/codemirror/addon/lint/yaml-lint.js \
	 node_modules/codemirror/addon/mode/simple.js \
	 node_modules/codemirror/mode/dockerfile/dockerfile.js \
	 node_modules/codemirror/mode/toml/toml.js \
	 node_modules/codemirror/mode/yaml/yaml.js \
	 node_modules/codemirror/addon/comment/comment.js \
   node_modules/jsonlint-mod/lib/jsonlint.js \
   | esbuild --log-level=$ESBuildLogLevel $minify >  $dist/htdocs/js/codemirror.min.js

cat \
  node_modules/pixl-webapp/js/md5.js \
  node_modules/pixl-webapp/js/oop.js \
  node_modules/pixl-webapp/js/xml.js \
  node_modules/pixl-webapp/js/tools.js \
  node_modules/pixl-webapp/js/datetime.js \
  node_modules/pixl-webapp/js/page.js \
  node_modules/pixl-webapp/js/dialog.js \
  node_modules/pixl-webapp/js/base.js \
  | esbuild --log-level=$ESBuildLogLevel $minify --keep-names >  $dist/htdocs/js/common.min.js

cat htdocs/js/app.js \
  htdocs/js/pages/Base.class.js \
  htdocs/js/pages/Home.class.js \
  htdocs/js/pages/Login.class.js \
  htdocs/js/pages/Schedule.class.js \
  htdocs/js/pages/History.class.js \
  htdocs/js/pages/JobDetails.class.js \
  htdocs/js/pages/MyAccount.class.js \
  htdocs/js/pages/Admin.class.js \
  htdocs/js/pages/admin/Categories.js \
  htdocs/js/pages/admin/Servers.js \
  htdocs/js/pages/admin/Users.js \
  htdocs/js/pages/admin/Plugins.js \
  htdocs/js/pages/admin/Activity.js \
  htdocs/js/pages/admin/APIKeys.js \
  htdocs/js/pages/admin/ConfigKeys.js \
  htdocs/js/pages/admin/Secrets.js \
  | esbuild --log-level=$ESBuildLogLevel $minify --keep-names >  $dist/htdocs/js/combo.min.js

cp htdocs/index-bundle.html  $dist/htdocs/index.html

writehead "Bundle storage-cli and event plugins"

esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/ \
 --external:../conf/config.json --external:../conf/storage.json --external:../conf/setup.json \
bin/storage-cli.js

esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  bin/shell-plugin.js
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  bin/test-plugin.js
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  bin/url-plugin.js
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  --loader:.node=file bin/ssh-plugin.js
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  --loader:.node=file bin/sshx-plugin.js
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  bin/workflow.js
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  bin/run-detached.js
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/  --loader:.node=file bin/docker-plugin.js

# ----------- Tools (repair/migrate)
if [ "$tools" = 1 ]; then
writehead "Bundling cronicle tools"
  printf "      - storage-repair.js \n"
  esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/ --external:../conf/config.json bin/storage-repair.js
  printf "      - storage-migrate.js \n"
  esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/ --external:../conf/config.json bin/storage-migrate.js
fi

writehead "Building Storage Engines"

printf "      - bundling FS Engine\n"
esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/engines engines/Filesystem.js

if [ "$s3" = 1 ]; then
   printf "      - bundling S3 Engine\n"
   npm i @aws-sdk/client-s3 @aws-sdk/lib-storage --no-save --loglevel silent 
   esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/engines engines/S3.js
fi

if [ "$level" = 1 ]; then
   printf "      - bundling Level Engine\n"
   npm i level --no-save --loglevel silent 
   esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/engines engines/Level.js
   mkdir -p $dist/bin/engines/prebuilds/
   cp -r node_modules/classic-level/prebuilds/linux-x64 $dist/bin/engines/prebuilds/
fi

if [ "$lmdb" = 1 ]; then
   printf "      - bundling Lmdb Engine*\n"
   esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/engines --external:lmdb engines/Lmdb.js 
fi

if [ "$sftp" = 1 ]; then
   printf "      - bundling Sftp Engine\n"
   npm i ssh2-sftp-client --no-save --loglevel silent 
   esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --loader:.node=file --outdir=$dist/bin/engines engines/Sftp.js
fi


# ----- SQL Storage Engine
sqlDrivers=()
sqlArgs=("--bundle" "--minify" "--platform=node" "--outdir=$dist/bin/engines")
# exclude unused drivers
sqlArgs+=("--external:better-sqlite3" "--external:mysql")

if [ "$sql" = 1 ]; then
  oracle=1
  mssql=1
  mysql=1
  pgsql=1
  sqlite=1
fi

# driver need to be installed separetly (cannot bundle native libs)

if [ "$mysql" = 1 ]; then
  sqlDrivers+=("mysql2")
else
  sqlArgs+=("--external:mysql2")
fi

if [ "$pgsql" = 1 ]; then
  sqlDrivers+=("pg" "pg-query-stream")
else
  sqlArgs+=("--external:pg" "--external:pg-query-stream")
fi

if [ "$oracle" = 1 ]; then
  sqlDrivers+=("oracledb@6.5.0")
else
  sqlArgs+=("--external:oracledb")
fi

if [ "$mssql" = 1 ]; then
  sqlDrivers+=("tedious")
else
  sqlArgs+=("--external:tedious")
fi

if [ "$sqlite" = 1 ]; then
  sqlDrivers+=("sqlite3")
else
  sqlArgs+=("--external:sqlite3")
fi

if [[ ${#sqlDrivers[@]} -gt 0 ]]; then
  sqlInstall=("install" "--no-save" "--loglevel" "silent" "knex")
  sqlInstall+=("${sqlDrivers[@]}")

  sqlArgs+=("engines/SQL.js")

  echo "      - bundling SQL Engine [${sqlDrivers[@]}]"
  npm "${sqlInstall[@]}"
  esbuild "${sqlArgs[@]}"
  if [ "$sqlite" = 1 ]; then
    cp -r node_modules/sqlite3/build $dist/bin/
  fi
fi

if [ "$redis" = 1 ]; then
   printf "      - bundling Redis Engine \n"
   npm i redis@3.1.2 --no-save --loglevel silent 
   esbuild --bundle --log-level=$ESBuildLogLevel $minify --platform=node --outdir=$dist/bin/engines engines/Redis.js
fi

# --- CRONICLE.JS
writehead "Bundling cronicle.js"
esbuild --bundle --log-level=$ESBuildLogLevel $minify --keep-names --platform=node --outfile=$dist/bin/cronicle.js lib/main.js

# no need to fix formidable since using 3.x version
#writehead "Applyig some patches"
# fix formidable 
#esbuild --bundle --log-level=$ESBuildLogLevel $minify --keep-names --platform=node --outdir=$dist/bin/plugins node_modules/formidable/src/plugins/*.js

# --- setup configs on the initial run
if [ ! -d $dist/conf ]; then
  writehead "Setting up initial configs"
  mkdir -p $dist/conf
  cp sample_conf/config.json sample_conf/setup.json $dist/conf/
  cp -r sample_conf/emails $dist/conf/emails
  cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1 > $dist/conf/secret_key
  chmod 400 $dist/conf/secret_key
fi

# -- override storage engine if specified
if [ -f "sample_conf/examples/storage.$engine.json" ]; then
  writehead "Overriding stoarge.json for $engine"
  cp "sample_conf/examples/storage.$engine.json" $dist/conf/storage.json
fi


# ---- set up npm pac in dist if not yet, add some deps if needed
cd $dist
if [ ! -e 'package.json' ]; then 
  writehead "Setting up npm package in $dist"
   npm init -y &>/dev/null
   npm pkg set name="croniclex"
   npm pkg set bin="bin/control.sh"
   npm pkg set main="bin/cronicle.js"
   npm pkg set scripts.start="bin/cronicle.js --foreground --echo --manager --color"
fi

if [ "$lmdb" = 1 ]; then
  npm i lmdb@2.9.4 --loglevel silent
fi 

cd - &>/dev/null  

# ------  final steps
writehead "Setting up permissions"
chmod -R 755 $dist/bin

# -------------------- we are done 

# if in dev mode, start cronicle on background in manager mode
if [ "$restart" = 1 ]; then
  writehead "Starting cronicle..."
  export CRONICLE_dev_version="1.x.dev-$(date '+%Y-%m-%d %H:%M:%S')"
  $dist/bin/cronicle.js --manager
 # echo "      - new process: $(cat $dist/logs/cronicled.pid)"
  echo "      - dev version: $CRONICLE_dev_version"
  exit 0  
fi

echo ""
echo -e "\033[1;4mBundle is ready: $(readlink -f $dist)\033[0m"
echo ""

if [ "$lmdb" = 1 ]; then
  echo " - Lmdb package was installed in $dist"
fi 


if [ "$restart" = 1 ]; then
 echo "Running in dev mode. Version: $CRONICLE_dev_version"
 exit 0
fi

if [ ! "$dev" = 1 ]; then
cat << EOF

Before you begin:
 - Configure you storage engine setting in conf/config.json || conf/storage.json || CRONICLE_storage_config=/path/to/custom/storage.json
 - Set you Secret key in conf/config.json || conf/secret_key file || CRONICLE_secret_key_file || CRONICLE_secret_key env variables

To setup cronicle storage (on the first run):
  node $dist/bin/storage-cli.js setup

Start as manager in foreground:
  node $dist/bin/cronicle.js --echo --foreground --manager --color

Use $dist/bin/manager entrypoint to complete both steps (use for dev or docker):
  $dist/bin/manager [--port 3012] [--storage /path/to/storage.json] [--key secretKey] [--sqlite /path/to/sqlite.db] [--color]

Or use control.sh tool: 
  $dist/bin/control.sh [setup|status|start|stop]

Use force flag (-f) to reinstall/upgrade (after git pull or npm package update): 
  ./bundle.sh $dist -f 

To setup as systemd service (! make sure node version for sudo user is 16 or higher):
  cd $(readlink -f $dist)
  npm i pixl-boot
  sudo node node_modules/pixl-boot/cli.js install
  ### systemctl status croniclex

To remove service:
  sudo node node_modules/pixl-boot/cli.js uninstall
EOF

fi

#  OPTIONAL UNIT TEST
if [ "$test" = 1 ]; then
  writehead " Bundling unit test files"
  esbuild --bundle --log-level=info --minify=false --platform=node --outdir=$dist/bin lib/test.js
  writehead " Running tests"
  node node_modules/pixl-unit/unit.js $dist/bin/test.js
  # remove test files
  rm $dist/bin/test.js
fi

